package com.sahyadri.web.google.calendar;

import com.google.gdata.client.Query;
import com.google.gdata.client.Query.CategoryFilter;
import com.google.gdata.client.calendar.CalendarQuery;
import com.google.gdata.client.calendar.CalendarService;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.Person;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.TextContent;
import com.google.gdata.data.calendar.CalendarEntry;
import com.google.gdata.data.calendar.CalendarFeed;
import com.google.gdata.data.calendar.CalendarEventEntry;
import com.google.gdata.data.calendar.CalendarEventFeed;
import com.google.gdata.data.calendar.EventWho;
import com.google.gdata.data.extensions.BaseEventEntry.Visibility;
import com.google.gdata.data.extensions.Recurrence;
import com.google.gdata.data.extensions.Reminder;
import com.google.gdata.data.extensions.When;
import com.google.gdata.data.extensions.Where;
import com.google.gdata.data.extensions.Who;
import com.google.gdata.data.extensions.Who.AttendeeStatus;
import com.sahyadri.web.bean.Events;
import com.sahyadri.web.util.service.adpater.OrganizationServiceAdapterImpl;

import java.io.PrintStream;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import org.apache.log4j.Logger;
import org.springframework.util.StringUtils;

/**
 * An application that serves as a sample to show how the CalendarService can be
 * used to create/read/update/delete data on Google Calendar.
 * 
 * 
 */
public class CalendarClientService {

	private static final String CALENDAR_FEEDS_URL = "http://www.google.com/calendar/feeds";
	private static final Logger log = Logger
			.getLogger(CalendarClientService.class);
	private CalendarService calendarService;
	private URL privateFeedUrl;
	
	{
		//System.setProperty("https.proxyHost", "corpgate.labcorp.com");
	    //System.setProperty("https.proxyPort", "8080"); 
	}

	public CalendarClientService(String username, String password)
			throws Exception {

		calendarService = new CalendarService("gdata-sample-calendar");
		calendarService.setUserCredentials(username, password);
		privateFeedUrl = new URL(CALENDAR_FEEDS_URL + "/default/private/full");
	}

	/**
	 * Driver for the sample.
	 * 
	 * @param out
	 *            outputStream to which to write status and messages
	 */
	public void run(PrintStream out) throws Exception {
		/*// Retrieve entire feed of events
		retrieveFeed(out);

		// Add an event
		CalendarEventEntry event = addEvent(out);

		// Send a full-text search query
		String attendeeEmail = event.getParticipants().get(0).getEmail();
		sendFullTextQuery(attendeeEmail, out);

		// Send a date-range query for all events that occur today
		Calendar todayCalendar = Calendar.getInstance();
		todayCalendar.set(Calendar.HOUR, 0);
		todayCalendar.set(Calendar.MINUTE, 0);
		todayCalendar.set(Calendar.SECOND, 0);
		todayCalendar.set(Calendar.AM_PM, Calendar.AM);
		DateTime startTime = new DateTime(new Date(
				todayCalendar.getTimeInMillis()));
		todayCalendar.set(Calendar.HOUR, 23);
		DateTime endTime = new DateTime(new Date(
				todayCalendar.getTimeInMillis()));
		sendDateRangeQuery(startTime, endTime, out);

		// Update an event
		event = updateEvent(event);

		// Delete an event
		log.debug("Deleting entry: " + event.getSelfLink().getHref() + ".\n");
		// deleteEntry(event.getEditLink().getHref());
		event.delete();
		// Insert recurring event
		//CalendarEventEntry recurringEvent = insertRecurringEvent(out);

		// Delete recurring event
		log.debug("Deleting entry: " + recurringEvent.getSelfLink().getHref()
				+ ".\n");
		// deleteE/ntry(recurringEvent.getEditLink().getHref());
		recurringEvent.delete();
		// List all calendars
		listAllCalendars(out);*/
	}

	/**
	 * Retrieves the default private feed.
	 * 
	 * @param out
	 *            outputStream on which to write status info
	 * @throws Exception
	 *             if error in retrieving feed
	 */
	public void retrieveFeed(PrintStream out) throws Exception {
		CalendarEventFeed myFeed = calendarService.getFeed(privateFeedUrl,
				CalendarEventFeed.class);
		log.debug("Retrieved feed with title: "
				+ myFeed.getTitle().getPlainText()
				+ "\nNumber of events in feed: " + myFeed.getEntries().size()
				+ "\n");
	}
	

	
	/**
	 * Updates the provided event by changing the details of the event.
	 * 
	 * @param entry
	 *            the entry to update
	 * @param out
	 *            outputStream on which to write status info
	 * @return the updated entry
	 * @throws Exception
	 *             if error on update
	 */
	public CalendarEventEntry updateEvent(CalendarEventEntry entry) throws Exception {
		log.debug("Updating CalendarEventEntry ");
		URL newEntryUrl = new URL(entry.getEditLink().getHref());
		CalendarEventEntry updatedEntry = calendarService.update(newEntryUrl, entry);
		showCalendarEventEntry(updatedEntry);
		return updatedEntry;
	}

	/**
	 * Perform a full text search over all the events.
	 * 
	 * @param queryString
	 *            the string to search
	 * @param out
	 *            outputStream on which to write status info
	 * @return the feed of results
	 * @throws Exception
	 *             if error in querying
	 */
	public CalendarEventFeed sendFullTextQuery(String queryString,
			PrintStream out) throws Exception {
		Query myQuery = new Query(privateFeedUrl);
		myQuery.setFullTextQuery(queryString);
		CalendarEventFeed resultFeed = calendarService.query(myQuery,
				CalendarEventFeed.class);

		log.debug("Full text search for query: \"" + queryString
				+ "\" and retrieved " + resultFeed.getEntries().size()
				+ " events.");

		if (resultFeed.getEntries().size() > 0) {
			log.debug("Title of first event: \""
					+ resultFeed.getEntries().get(0).getTitle().getPlainText()
					+ "\".");
		}
		return resultFeed;
	}
	
	/**
	 * Perform a full text search over all the events.
	 * 
	 * @param queryString
	 *            the string to search
	 * @param out
	 *            outputStream on which to write status info
	 * @return the feed of results
	 * @throws Exception
	 *             if error in querying
	 */
	public CalendarEventEntry sendFullTextQuery(Events event) throws Exception {
		Query myQuery = new Query(privateFeedUrl);
		myQuery.setFullTextQuery(event.getHostEmailId());
		CalendarEventFeed resultFeed = calendarService.query(myQuery,
				CalendarEventFeed.class);
		CalendarEventEntry eventEntry = null;
		log.debug("Full text search for query: \"" + event.getHostEmailId()
				+ "\" and retrieved " + resultFeed.getEntries().size()
				+ " events.");

		if (resultFeed.getEntries().size() > 0) {
			for(CalendarEventEntry entry:resultFeed.getEntries()){
				if(entry.getTitle().getPlainText().equalsIgnoreCase(event.getName())){
					return entry;
				}
			}
		}
		return eventEntry;
	}

	
	private static void showCalendarEventEntry(CalendarEventEntry entry) {
	    assert entry != null;
	    System.out.println("-------------------------------------------");
	    System.out.println("START showCalendarEventEntry");
	    System.out.println("");
	    System.out.println("ID: " + entry.getId());
	    System.out.println("TITLE: "+entry.getTitle().getPlainText());
	    System.out.println("DESCRIPTION: "+entry.getPlainTextContent());
	    System.out.println("LOCATION: "+entry.getLocations().get(0).getValueString());

	    System.out.println("");
	    System.out.println("TIMES");
	    if (entry.getTimes().size() > 0) {
	        When eventTimes = entry.getTimes().get(0);
	        if (eventTimes.getStartTime().isDateOnly()) {
	            System.out.println("\tWHEN: ALL DAY");
	        } else {
	            System.out.println("\tWHEN: TIME");
	        } 

	        if (entry.getRecurrence() != null)
	            System.out.println("\tRECURRENCE: "+entry.getRecurrence().toString()); 

	        System.out.println("\tSTART TIME: "+eventTimes.getStartTime());
	        System.out.println("\tEND TIME: "+eventTimes.getEndTime());
	    }

	    System.out.println("");
	    System.out.println("PARTICIPANTS");
	    System.out.println("\t"+(entry.getParticipants().size()) + " PARTICIPANTS");
	    if (entry.getParticipants().size() > 0){

	        for (int i=0; i<entry.getParticipants().size(); i++) {
	            EventWho participant = entry.getParticipants().get(i);
	            System.out.println("\t\tPARTICIPANT "+participant.getValueString());
	            System.out.println("\t\t\tTYPE: "+participant.getAttendeeType());
	            System.out.println("\t\t\tSTATUS: "+participant.getAttendeeStatus());
	        }
	        if (entry.isGuestsCanInviteOthers())
	            System.out.println("\tGUESTS CAN INVITE OTHERS: ");
	        if (entry.isGuestsCanModify())
	            System.out.println("\tGUESTS CAN MODIFY");
	        if (entry.isGuestsCanSeeGuests())
	            System.out.println("\tGUESTS CAN SEE GUESTS");
	    } 

	    //REMINDERS
	    System.out.println("");
	    System.out.println("REMINDERS");
	    System.out.println("\t"+entry.getReminder().size()+" REMINDERS");
	    if (entry.getReminder().size() > 0) {
	        for (int i=0; i<entry.getReminder().size(); i++) {
	            Reminder reminder = entry.getReminder().get(i);
	            System.out.println("\t\tREMINDER "+i);
	            System.out.println("\t\t\tMETHOD: "+reminder.getMethod().toString());
	            System.out.println("\t\t\tDAYS: "+reminder.getDays());
	            System.out.println("\t\t\tHOURS: "+reminder.getHours());
	            System.out.println("\t\t\tMINUTES: "+reminder.getMinutes());                
	        }
	    }

	    //VISIBILITY
	    System.out.println("");
	    System.out.println("VISIBILITY: "+entry.getVisibility().getValue());

	    System.out.println("");
	    System.out.println("END showCalendarEventEntry");
	    System.out.println("-------------------------------------------");
	}
	/**
	 * Insert an entry with the given parameters.
	 * 
	 * @return the inserted entry
	 * @throws Exception
	 *             if error on insert
	 */
	public CalendarEventEntry insertEntry(CalendarEventEntry myEntry)
			throws Exception {
		// Insert entry
		CalendarEventEntry insertedEntry = calendarService.insert(privateFeedUrl, myEntry);
		showCalendarEventEntry(insertedEntry);
		return insertedEntry;
	}

	/**
	 * Creates an object that contains the provided parameters.
	 * 
	 * @return an CalendarEventEntry object with the given parameters
	 */
	public CalendarEventEntry createEntryObject(String titleText,
			String contentText, String authorName, String authorEmail,
			DateTime startTime, DateTime endTime, String recurData,
			Map<String, String> attendeeList, int reminderMinutes) {
		// Set title and content
		CalendarEventEntry myEntry = new CalendarEventEntry();
		myEntry.setTitle(new PlainTextConstruct(titleText));
		if (contentText != null) {
			myEntry.setContent(new PlainTextConstruct(contentText));
		}

		// Set author
		if (authorName != null) {
			Person author = new Person(authorName, null, authorEmail);
			myEntry.getAuthors().add(author);
		}

		// Set recurrence
		if (recurData != null) {
			Recurrence recur = new Recurrence();
			recur.setValue(recurData);
			myEntry.setRecurrence(recur);
		}

		// Set reminder for event
		if (reminderMinutes > 0) {
			Reminder reminder = new Reminder();
			reminder.setMinutes(reminderMinutes);
			myEntry.addExtension(reminder);
		}

		// Set start and end time
		if (startTime != null || endTime != null) {
			When eventTime = new When();
			if (startTime != null) {
				eventTime.setStartTime(startTime);
			}
			if (endTime != null) {
				eventTime.setEndTime(endTime);
			}
			myEntry.addTime(eventTime);
		}

		// Set the attendess
		if (attendeeList != null) {
			for (Map.Entry<String, String> entry : attendeeList.entrySet()) {
				EventWho who = new EventWho();
				who.setEmail(entry.getKey());
				who.setAttendeeStatus(entry.getValue());
				myEntry.getParticipants().add(who);
			}
		}
		return myEntry;
	}

	/**
	 * Creates an object that contains the provided parameters.
	 * 
	 * @return an CalendarEventEntry object with the given parameters
	 */
	public CalendarEventEntry createEntryObject(Events event) {
		// Set title and content
		CalendarEventEntry myEntry = new CalendarEventEntry();
		myEntry.setTitle(new PlainTextConstruct(event.getName()));
		if (StringUtils.hasText(event.getDescription())) {
			myEntry.setContent(new PlainTextConstruct(event.getDescription()));
		}

		// Set author
		if (StringUtils.hasText(event.getHostName())) {
			Person author = new Person(event.getHostName(), null, event.getHostEmailId());
			myEntry.getAuthors().add(author);
		}

		// Set recurrence
		/*if (recurData != null) {
			Recurrence recur = new Recurrence();
			recur.setValue(recurData);
			myEntry.setRecurrence(recur);
		}*/

		// Set reminder for event
		//if (reminderMinutes > 0) {
			Reminder reminder = new Reminder();
			reminder.setMinutes(30);
			myEntry.addExtension(reminder);
		//}
			When eventTime = new When();
			DateTime startTime = null;
			DateTime endTime = null;
		// Set start and end time
		if (event.getStartDate() != null) {
			Calendar startDateCalendar = new GregorianCalendar(TimeZone.getTimeZone("IST"));
			startDateCalendar.setTime (event.getStartDate());
			
			if(StringUtils.hasText(event.getOpenTime()) && event.getOpenTime().length() == 8) {
				startDateCalendar.set(Calendar.HOUR, new Integer(event.getOpenTime().substring(0, 2)).intValue());
				startDateCalendar.set(Calendar.MINUTE, new Integer(event.getOpenTime().substring(0, 2)).intValue());
				startDateCalendar.set(Calendar.SECOND, 0);
				startDateCalendar.set(Calendar.AM_PM, event.getOpenTime().substring(6, 8).equals("AM")?Calendar.AM:Calendar.PM);
			}else{
				startDateCalendar.set(Calendar.HOUR, 11);
				startDateCalendar.set(Calendar.MINUTE, 30);
				startDateCalendar.set(Calendar.SECOND, 0);
				startDateCalendar.set(Calendar.AM_PM, Calendar.AM);
			}
			System.out.println(startDateCalendar.getTime());
			startTime = new DateTime(new Date(startDateCalendar.getTimeInMillis()));
		}
		
		// Set start and end time
				if (event.getEndDate() != null) {
					Calendar endDateCalendar = new GregorianCalendar(TimeZone.getTimeZone("IST"));
					endDateCalendar.setTime(event.getEndDate());
					endDateCalendar.setTimeZone(TimeZone.getTimeZone("IST"));
					
					if(StringUtils.hasText(event.getCloseTime()) && event.getCloseTime().length() == 8) {
						endDateCalendar.set(Calendar.HOUR, new Integer(event.getCloseTime().substring(0, 2)).intValue());
						endDateCalendar.set(Calendar.MINUTE, new Integer(event.getCloseTime().substring(0, 2)).intValue());
						endDateCalendar.set(Calendar.SECOND, 0);
						endDateCalendar.set(Calendar.AM_PM, event.getCloseTime().substring(6, 8).equals("AM")?Calendar.AM:Calendar.PM);
					}else{
						endDateCalendar.set(Calendar.HOUR, 12);
						endDateCalendar.set(Calendar.MINUTE, 30);
						endDateCalendar.set(Calendar.SECOND, 0);
						endDateCalendar.set(Calendar.AM_PM, Calendar.PM);
					}
					System.out.println(endDateCalendar.getTime());
					endTime = new DateTime(new Date(endDateCalendar.getTimeInMillis()));
				}
			if (startTime != null) {
				eventTime.setStartTime(startTime);
			}
			if (endTime != null) {
				eventTime.setEndTime(endTime);
			}
			myEntry.addTime(eventTime);
		
		// Add host email id has Attendee also
			EventWho host = new EventWho();
			host.setEmail(event.getHostEmailId());
			host.setAttendeeStatus(AttendeeStatus.EVENT_INVITED);
			myEntry.getParticipants().add(host);
			
		// Set the attendess
		if (StringUtils.hasText(event.getAttendeeEmailIds())) {
			String[] attendeeArray = event.getAttendeeEmailIds().split("\\,");
			for(String emailId:attendeeArray) {
				EventWho who = new EventWho();
				who.setEmail(emailId);
				who.setAttendeeStatus(AttendeeStatus.EVENT_INVITED);
				myEntry.getParticipants().add(who);
			}
		}
		//myEntry.setVisibility(event.isPublicEvent()?Visibility.PUBLIC:Visibility.DEFAULT);
		
		return myEntry;
	}
	
	/**
	 * Update an object that contains the provided parameters.
	 * 
	 * @return an CalendarEventEntry object with the given parameters
	 */
	public CalendarEventEntry updateEntryObject(CalendarEventEntry myEntry, Events event) {
		// Set title and content
		myEntry.setTitle(new PlainTextConstruct(event.getName()));
		if (StringUtils.hasText(event.getDescription())) {
			myEntry.setContent(new PlainTextConstruct(event.getDescription()));
		}

		// Set author
		if (StringUtils.hasText(event.getHostName())) {
			Person author = new Person(event.getHostName(), null, event.getHostEmailId());
			myEntry.getAuthors().add(author);
		}

		// Set recurrence
		/*if (recurData != null) {
			Recurrence recur = new Recurrence();
			recur.setValue(recurData);
			myEntry.setRecurrence(recur);
		}*/

		// Set reminder for event
		//if (reminderMinutes > 0) {
			Reminder reminder = new Reminder();
			reminder.setMinutes(30);
			myEntry.addExtension(reminder);
		//}
			When eventTime = new When();
			DateTime startTime = null;
			DateTime endTime = null;
		// Set start and end time
		if (event.getStartDate() != null) {
			Calendar startDateCalendar = new GregorianCalendar();
			startDateCalendar.setTime (event.getStartDate());
			if(StringUtils.hasText(event.getOpenTime()) && event.getOpenTime().length() == 8) {
				startDateCalendar.set(Calendar.HOUR, new Integer(event.getOpenTime().substring(0, 2)).intValue());
				startDateCalendar.set(Calendar.MINUTE, new Integer(event.getOpenTime().substring(0, 2)).intValue());
				startDateCalendar.set(Calendar.SECOND, 0);
				startDateCalendar.set(Calendar.AM_PM, event.getOpenTime().substring(6, 8).equals("AM")?Calendar.AM:Calendar.PM);
			}else{
				startDateCalendar.set(Calendar.HOUR, 11);
				startDateCalendar.set(Calendar.MINUTE, 30);
				startDateCalendar.set(Calendar.SECOND, 0);
				startDateCalendar.set(Calendar.AM_PM, Calendar.AM);
			}
			startTime = new DateTime(new Date(	startDateCalendar.getTimeInMillis()));
		}
		
		// Set start and end time
				if (event.getEndDate() != null) {
					Calendar endDateCalendar = new GregorianCalendar();
					endDateCalendar.setTimeZone(TimeZone.getTimeZone("IST"));
					endDateCalendar.setTime (event.getStartDate());
					if(StringUtils.hasText(event.getCloseTime()) && event.getCloseTime().length() == 8) {
						endDateCalendar.set(Calendar.HOUR, new Integer(event.getCloseTime().substring(0, 2)).intValue());
						endDateCalendar.set(Calendar.MINUTE, new Integer(event.getCloseTime().substring(0, 2)).intValue());
						endDateCalendar.set(Calendar.SECOND, 0);
						endDateCalendar.set(Calendar.AM_PM, event.getCloseTime().substring(6, 8).equals("AM")?Calendar.AM:Calendar.PM);
					}else{
						endDateCalendar.set(Calendar.HOUR, 11);
						endDateCalendar.set(Calendar.MINUTE, 30);
						endDateCalendar.set(Calendar.SECOND, 0);
						endDateCalendar.set(Calendar.AM_PM, Calendar.PM);
					}
					endTime = new DateTime(new Date(endDateCalendar.getTimeInMillis()));
				}
			if (startTime != null) {
				eventTime.setStartTime(startTime);
			}
			if (endTime != null) {
				eventTime.setEndTime(endTime);
			}
			myEntry.addTime(eventTime);
		

		// Set the attendess
		if (StringUtils.hasText(event.getAttendeeEmailIds())) {
			String[] attendeeArray = event.getAttendeeEmailIds().split("\\,");
			for(String emailId:attendeeArray) {
				EventWho who = new EventWho();
				who.setEmail(emailId);
				who.setAttendeeStatus(AttendeeStatus.EVENT_INVITED);
				myEntry.getParticipants().add(who);
			}
		}
		//myEntry.setVisibility(event.isPublicEvent()?Visibility.PUBLIC:Visibility.DEFAULT);
		
		return myEntry;
	}
	/**
	 * Perform a date range query over the events in the calendar.
	 * 
	 * @param min
	 *            the minimum date. (may be null)
	 * @param max
	 *            the maximum date. (may be null)
	 * @param out
	 *            outputStream on which to write status info
	 * @return the feed of results
	 * @throws Exception
	 *             if error in querying
	 */
	public CalendarEventFeed sendDateRangeQuery(DateTime min, DateTime max,
			PrintStream out) throws Exception {
		CalendarQuery myQuery = new CalendarQuery(privateFeedUrl);
		myQuery.setMinimumStartTime(min);
		myQuery.setMaximumStartTime(max);
		CalendarEventFeed resultFeed = calendarService.query(myQuery,
				CalendarEventFeed.class);

		log.debug("Date range query (from, to): (" + min + " , " + max + ")"
				+ " returned " + resultFeed.getEntries().size() + " events.");

		if (resultFeed.getEntries().size() > 0) {
			log.debug("Title of first event: \""
					+ resultFeed.getEntries().get(0).getTitle().getPlainText()
					+ "\".");
		}
		return resultFeed;
	}

	/**
	 * Deletes the specified entry.
	 * 
	 * @param entryUrlString
	 *            the edit URL of the entry to delete
	 * @throws Exception
	 *             if error in delete
	 */
	public void deleteEntry(String entryUrlString) throws Exception {
		URL entryUrl = new URL(entryUrlString);
		calendarService.delete(entryUrl);
	}

	/**
	 * Lists all the calendars owned by the user.
	 * 
	 * @param out
	 *            outputStream on which to write status info
	 * @throws Exception
	 *             if error in retrieving the calendars
	 */
	public void listAllCalendars(PrintStream out) throws Exception {
		URL calendarsUrl = new URL(CALENDAR_FEEDS_URL + "/default");
		new CalendarFeed().declareExtensions(calendarService
				.getExtensionProfile());
		CalendarFeed feed = calendarService.getFeed(calendarsUrl,
				CalendarFeed.class);
		log.debug("List of calendars for account:");
		for (int entry_i = 0; entry_i < feed.getEntries().size(); entry_i++) {
			CalendarEntry calEntry = feed.getEntries().get(entry_i);
			log.debug("\"" + calEntry.getTitle().getPlainText() + "\"\t"
					+ calEntry.getSelfLink().getHref());
		}
	}

	/**
	 * Main entry point. Parses arguments and creates and invokes the
	 * CalendarClient.
	 */
	public static void main(String[] arg) throws Exception {

		String username = "sahyadrirestaurant@gmail.com";
		String password = "sahyadri28122012";

		CalendarClientService client = new CalendarClientService(username,
				password);
		client.run(System.out);
	}

	/**
	 * Prints usage of this application.
	 */
	public static void usage() {
		log.debug("Usage: java CalendarClient --username [user] "
				+ "--password [pass]");
		log.debug("\nA simple application that uses the provided Google\n"
				+ "Account username and password to perform simple\n"
				+ "create/read/update/delete operations against the user's\n"
				+ "Google Calendar account.\n");
	}
}
